-- UIDialogue
-- Created by chendh
-- 通用对话模板窗口

require "game/ui/module/guide/GuideSlimeDisplay"

UIDialogue = class("UIDialogue", function()
    return cc.Layer:create();
end);

function UIDialogue.create(templateId, callback, extraData)
    return UIDialogue.new(templateId, callback, extraData);
end

-- 构造函数
function UIDialogue:ctor(templateId, callback, extraData)
    self:setName("UIDialogue");
    local node = cc.CSLoader:createNode("layout/main/Dialogue.csb");
    self:addChild(node);
    self.node = node;
    self.templateId = templateId;
    self.callback = callback;
    self.extraData = extraData or {};

    self.panel = findChildByName(self.node, "CT/panel");

    self:setLocalZOrder(UIMgr.BONUS_NOTIFY_ZORDER + 1);

    -- 初始化
    self:init();

    -- 开始对话
    self:startDialogue();

    -- 适配
    self:resize();
end

-- 初始化
function UIDialogue:init()
    -- 对话模板数据
    local templateInfo = DialogueTemplateM.query(self.templateId);
    self.templateInfo = templateInfo;
    -- 初始化对话模板显示进度
    local curOrder = 0;
    self.curOrder = curOrder;

    if not self.templateInfo then
        -- 有问题。没有对白内容，结束对话
        self:endDialogue();
    end

    -- 背景透明度
    local formBg = findChildByName(self.node, "bg/bgImg");
    formBg:setOpacity(1);

    -- 注册点击处理
    local formBg = findChildByName(self.node, "bg/bgImg");
    formBg:addTouchEventListener(function() end);

    -- 记录老的文本框位置
    local bg = findChildByName(self.node, "CT/dialogue/dialog1");
    local textLabel = findChildByName(bg, "panel/text");
    self.oldTextLabelPos = cc.p(textLabel:getPosition());
end

-- 获取发言的冈布奥编号
function UIDialogue:querySpeakerId(curDialogueInfo)
    if curDialogueInfo["speaker"] == "active_pet" then
        -- 取当前出战的冈布奥编号
        local pet = ME.user:getActivePet();
        return pet.classId;
    end

    return curDialogueInfo["speaker"];
end

-- 获取发言角色的名称
function UIDialogue:querySpeakerName(curDialogueInfo)
    if type(curDialogueInfo["speaker_name"]) == "string" and string.len(curDialogueInfo["speaker_name"]) > 0 then
        -- 配置了名称，直接使用
        return curDialogueInfo["speaker_name"];
    end

    -- 使用冈布奥的名称
    local speakerId = self:querySpeakerId(curDialogueInfo)
    return PetM.query(speakerId, "name");
end

-- 开始对话
function UIDialogue:startDialogue()
    self.curOrder = self.curOrder + 1;
    local curDialogueInfo = self.templateInfo[self.curOrder];

    if not curDialogueInfo then
        -- 对白已结束，结束对话
        self:endDialogue();
        return;
    end

    if curDialogueInfo["pos"] == self.curPos and
       self:querySpeakerId(curDialogueInfo) == self.curSpeaker then
        -- 上一次的发言者在原地进行发言，不需要飞入效果
        -- 开始对话
        self:talk(true);
    else
        -- 飞入效果
        local function callback()
            -- 开始对话
            self:talk();
        end

        if self.curPos and self.curSpeaker then
            -- 如果当前有发言者，那么先飞出，再飞入
            self:runOutEffect(function ()
                -- 飞入
                self:runInEffect(callback);
            end);
        else
            -- 当前没有发言者，直接飞入
            self:runInEffect(callback);
        end

    end
end

-- 结束对话
function UIDialogue:endDialogue()
    local function endAndCallback()
        -- 调用回调
        if self.callback then
            self.callback();
        end

        -- 关闭界面
        UIMgr.getCurrentScene():removeFormByName("UIDialogue");
    end

    self:runOutEffect(endAndCallback)
end

-- 初始化对话内容
function UIDialogue:initDialogue()
    local curDialogueInfo = self.templateInfo[self.curOrder];

    if not curDialogueInfo then
        -- 对白已结束，结束对话
        self:endDialogue();
        return;
    end

    self.dialog = findChildByName(self.node, "CT/dialogue/dialog" .. curDialogueInfo["pos"]);
    self.dialog:setVisible(true);
    self.textLabel = findChildByName(self.dialog, "panel/text");

    -- 还原位置
    self.textLabel:setPosition(self.oldTextLabelPos);

    -- 保存配置要显示的pos及发言者
    self.curPos = curDialogueInfo["pos"];
    local curSpeaker = self:querySpeakerId(curDialogueInfo);
    if curSpeaker <= 0 then
        self.curSpeaker = ME.user:getActivePet().classId;
    else
        self.curSpeaker = curSpeaker;
    end

    -- 冈布奥名称
    local nameLabel = findChildByName(self.dialog, "name");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_GREEN, false);
    local name = self:querySpeakerName(curDialogueInfo);
    nameLabel:setString(name);

    -- 初始隐藏箭头
    local arrow = findChildByName(self.dialog, "arrow");
    arrow:setVisible(false);

    -- 说话内容
    local textScale = 1;
    if getLang() == "th" then
        textScale = 0.85;
    end
    TextStyleM.setTextStyle(self.textLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_DARK_BROWN, false, nil, nil, nil, nil, TextStyleM.TEXT_SIZE_SMALL, nil, textScale);

    local content = curDialogueInfo["content"];

    for key, value in pairs(self.extraData) do
        if type(key) == "string" and type(value) == "string" then
            content = string.gsub(content, string.format("{%s}", key), value);
        end
    end

    self.textLabel = applyString(self.textLabel, content);

    -- 收集字符列表
    self:collectLetters(content);
end

-- 开始对话
function UIDialogue:talk(newDialog)
    -- 初始化对话内容
    self:initDialogue();
    -- 点击处理函数
    local function onClick(sender, eventType)
        if eventType == ccui.TouchEventType.began then
        elseif eventType == ccui.TouchEventType.ended then
            if not self.isFinished then
                -- 略过该段对话
                self:skipSection();
            else
                -- 开始下一段对话
                self:startDialogue();
            end
        end
    end

    -- 注册点击处理
    local formBg = findChildByName(self.node, "bg/bgImg");
    formBg:addTouchEventListener(onClick);

    local dialog = findChildByName(self.node, "CT/dialogue/dialog" .. self.curPos);

    -- 泡泡先冒出来
    dialog:setVisible(true);

    if newDialog then
        self:playTextEffect();

    else
        dialog:setScale(0.3);
        local scaleTo = cc.ScaleTo:create(0.1, 1);

        local callFunc = cc.CallFunc:create(function()
        -- 播放文字效果
            self:playTextEffect();
        end);
        dialog:runAction(cc.Sequence:create(scaleTo, callFunc));
    end


end

-- 收集字符列表
function UIDialogue:collectLetters(text)
    local textLabel = self.textLabel;
    local count = string.getWCharCount(text);
    self.letters = {};

    local i = 0;
    local endPos = count - 1;
    local charPos = 0;
    local curPaddingCount = 0;
    local lineNum = 0;
    while i <= endPos do
        local letter = textLabel:getVirtualRenderer():getLetter(i);
        local nextLetter = textLabel:getVirtualRenderer():getLetter(i+1);
        if letter then
            -- 初始所有字符置为不可见
            letter:setOpacity(0);
            letter.lineNum = lineNum;

            -- 加入到字符列表
            table.insert(self.letters, 1, i);
        else
            if nextLetter ~= nil then
                lineNum = lineNum + 1;

                if not string.isBreakLineChar(text, charPos-curPaddingCount) then
                    -- 如果是系统自动添加的换行符，需要把结束位置往后挪
                    endPos = endPos + 1;
                    curPaddingCount = curPaddingCount + 1;
                end
            end
        end

        i = i + 1;
        charPos = charPos + 1;
    end
end

-- 对话效果，一个字一个字慢慢播放
function UIDialogue:playTextEffect()
    -- 开始说话
    if self.display then
        self.display:gotoTalk();
    end

    local duration = 0.1;
    if getLang() ~= "zh" and getLang() ~= "tw" then
        duration = 0.05;
    end
    local function play()
        if #self.letters > 0 then
            local pos = self.letters[#self.letters];
            local letter = self.textLabel:getVirtualRenderer():getLetter(pos);
            table.remove(self.letters, #self.letters);
            local fadeIn = cc.FadeIn:create(duration);
            local callFunc = cc.CallFunc:create(function()
                if 0 == #self.letters then
                    print("finished");

                    -- 箭头开始上下动
                    self:playArrowEffect();

                    -- 停止说话
                    if self.display then
                        self.display:gotoIdle();
                    end

                    -- 标记字体播放完成
                    self.isFinished = true;
                end
            end);

            self.isFinished = false;
            if letter then
                letter:runAction(cc.Sequence:create(fadeIn, callFunc));
            end
            performWithDelay(self.textLabel, play, duration);
        end
    end

    play();
end

-- 文字立刻显示
function UIDialogue:skipSection()
    self.textLabel:stopAllActions();

    local dialogNode1 = findChildByName(self.node, "CT/dialogue/dialog1");
    local dialogNode2 = findChildByName(self.node, "CT/dialogue/dialog2");
    dialogNode1:setVisible(false);
    dialogNode2:setVisible(false);

    local dialog = findChildByName(self.node, "CT/dialogue/dialog" .. self.curPos);
    dialog:setVisible(true);

    while #self.letters > 0 do
        local pos = self.letters[#self.letters];
        local letter = self.textLabel:getVirtualRenderer():getLetter(pos);
        table.remove(self.letters, #self.letters);

        if letter then
            letter:setOpacity(255);
        end

        if 0 == #self.letters then
            print("finished");

            -- 箭头开始上下动
            self:playArrowEffect();

            -- 标记字体播放完成
            self.isFinished = true;
        end
    end
end

-- 箭头上下移动
function UIDialogue:playArrowEffect()
    local dialog = findChildByName(self.node, "CT/dialogue/dialog" .. self.curPos);
    local arrow = findChildByName(dialog, "arrow");
    arrow:setVisible(true);
    if not arrow.isPlaying then
        local oldPos = cc.p(arrow:getPosition());

        local moveDown = cc.MoveTo:create(0.5, cc.pAdd(oldPos, cc.p(0, -5)));
        local moveBack = cc.MoveTo:create(0.5, oldPos);
        arrow:runAction(cc.RepeatForever:create(cc.Sequence:create(moveDown, moveBack)));
        arrow.isPlaying = true;
    end
end

-- 适配
function UIDialogue:resize()
    local node = self.node;

    AlignM.fitToScreen(node, "bg");

    -- CT中的蒙版适配
    local mask = findChildByName(self.node, "CT/dialogue/mask");
    local w = AlignM.frameSize.width;
    local h = AlignM.frameSize.height;
    local scale = math.min(w / DESIGN_WIDTH, h / DESIGN_HEIGHT);
    mask:setContentSize(w / scale, h / scale);
    mask:setOpacity(150);

    AlignM.alignToCenter(node, "CT");
end

-- 运行入场效果
function UIDialogue:runInEffect(callback)
    local curDialogueInfo = self.templateInfo[self.curOrder];

    if not curDialogueInfo then
        -- 对白已结束，结束对话
        self:endDialogue();
        return;
    end

    -- 入场效果需要使用接下来这句发言的冈布奥播放效果
    self.petNode = findChildByName(self.node, "CT/dialogue/pet" .. curDialogueInfo["pos"]);
    self.petNode:setVisible(true);

    -- 创建冈布奥模型
    local headIcon = findChildByName(self.petNode, "head_icon");
    local headModel = findChildByName(self.petNode, "head_model");

    headModel:removeAllChildren();

    local heroId = self:querySpeakerId(curDialogueInfo);
    if curDialogueInfo["status"] == "surprised" and heroId == 1 then
        -- 是冈布奥吃惊的表情

        self.display = GuideSlimeDisplay.create(4);
        headModel:addChild(self.display);
        headIcon:setVisible(false);

        self.display:setPositionY(self.display:getPositionY() - 100);

          -- 如果是在左侧的冈布奥，那么需要翻转一下
        if curDialogueInfo["pos"] == 1 and self.display:getScaleX() > 0 then
             self.display:setScaleX(self.display:getScaleX() * -1);
        end
    else
        if heroId <= 0 then
            -- 取出战冈布奥
            local pet = ME.user:getActivePet();
            heroId = pet.classId;
        end

        local offsetX = PetM.query(heroId, "offset_x");
        local offsetY = PetM.query(heroId, "offset_y");

        local iconPath, modelAnim;
        local iconId;
        local onlyImage = false;

        local shapeId = ShapeChangeM.getShapeChangeId(heroId);
        if type(shapeId) == "number" and shapeId ~= -1 then
            iconId = ShapeChangeM.query(shapeId, "icon");
            if iconId == nil then
                onlyImage = true;
                iconId = heroId;
            end
            iconPath = getHeroLargeIconPath(iconId);
            local animName = ShapeChangeM.query(shapeId, "animation");
            modelAnim = createShapeChangeMode(shapeId, headIcon, animName, headModel, 0.8, iconPath, offsetX, offsetY, onlyImage);
        else
            iconId = PetM.getDefaultIcon(heroId);
            if iconId == nil then
                onlyImage = true;
                iconId = heroId;
            end
            iconPath = getHeroLargeIconPath(iconId);
            local modelId = PetM.query(heroId, "default_model");
            modelAnim = createHeroModel(modelId, headIcon, headModel, 0.8, iconPath, offsetX, offsetY, onlyImage);
        end

        headIcon:setScale(0.8);
        if modelAnim ~= nil then
            self.display = modelAnim:getChildByName("HeroDisplay");
        end

        -- 如果是在左侧的冈布奥，那么需要翻转一下
        if curDialogueInfo["pos"] == 1 then
            if modelAnim ~= nil and modelAnim:getScaleX() > 0 then
                modelAnim:setScaleX(modelAnim:getScaleX() * -1);
            else
                headIcon:setScaleX(headIcon:getScaleX() * -1);
            end
        end
    end

    if curDialogueInfo["pos"] == 1 then
        -- 左侧飞入
        self:leftFlyIn(callback);
    elseif curDialogueInfo["pos"] == 2 then
        -- 右侧飞入
        self:rightFlyIn(callback);
    else
        print("入场效果未支持：pos = " .. curDialogueInfo["pos"]);
    end
    AudioM.playFx("button_tiny");
end

-- 左侧飞入
function UIDialogue:leftFlyIn(callback)
    -- 显示panel
    self.panel:setVisible(true);

    local petNode2 = findChildByName(self.node, "CT/dialogue/pet2");
    local dialogNode1 = findChildByName(self.node, "CT/dialogue/dialog1");
    local dialogNode2 = findChildByName(self.node, "CT/dialogue/dialog2");

    petNode2:setVisible(false);
    dialogNode1:setVisible(false);
    dialogNode2:setVisible(false);

    -- 左侧飞入
    local petNode1 = findChildByName(self.node, "CT/dialogue/pet1");
    petNode1:setVisible(true);
    petNode1:setOpacity(0);

    if not petNode1.initPosX then
        petNode1.initPosX = petNode1:getPosition();
    end

    petNode1:setPositionX(petNode1.initPosX - DESIGN_WIDTH / 2);
    local fadeIn = cc.FadeIn:create(0.2);
    local moveBy = cc.MoveBy:create(0.2, cc.p(DESIGN_WIDTH / 2, 0));
    local callFunc = cc.CallFunc:create(function()
        -- 隐藏panel
        self.panel:setVisible(false);

        callback();
    end);

    petNode1:runAction(cc.Sequence:create(cc.Spawn:create(fadeIn, moveBy), callFunc));
end

-- 右侧飞入
function UIDialogue:rightFlyIn(callback)
    -- 显示panel
    self.panel:setVisible(true);

    local petNode1 = findChildByName(self.node, "CT/dialogue/pet1");
    local dialogNode1 = findChildByName(self.node, "CT/dialogue/dialog1");
    local dialogNode2 = findChildByName(self.node, "CT/dialogue/dialog2");

    petNode1:setVisible(false);
    dialogNode1:setVisible(false);
    dialogNode2:setVisible(false);

    -- 右侧飞入
    local petNode2 = findChildByName(self.node, "CT/dialogue/pet2");
    petNode2:setVisible(true);
    petNode2:setOpacity(0);

    if not petNode2.initPosX then
        petNode2.initPosX = petNode2:getPosition();
    end

    petNode2:setPositionX(petNode2.initPosX + DESIGN_WIDTH / 2);
    local fadeIn = cc.FadeIn:create(0.2);
    local moveBy = cc.MoveBy:create(0.2, cc.p(-1 * DESIGN_WIDTH / 2, 0));
    local callFunc = cc.CallFunc:create(function()
        -- 隐藏panel
        self.panel:setVisible(false);

        callback();
    end);

    petNode2:runAction(cc.Sequence:create(cc.Spawn:create(fadeIn, moveBy), callFunc));
end

-- 运行离开效果
function UIDialogue:runOutEffect(callback)
    -- 飞出效果直接使用已结束的发言者来播放效果
    if self.curPos == 1 then
        -- 左侧飞出
        self:leftFlyOut(callback);
    elseif self.curPos == 2 then
        -- 右侧飞出
        self:rightFlyOut(callback);
    else
        print("出场效果未支持：pos = " .. self.curPos);
    end
end

-- 左侧飞出
function UIDialogue:leftFlyOut(callback)
    -- 显示panel
    self.panel:setVisible(true);

    local petNode2 = findChildByName(self.node, "CT/dialogue/pet2");
    local dialogNode1 = findChildByName(self.node, "CT/dialogue/dialog1");
    local dialogNode2 = findChildByName(self.node, "CT/dialogue/dialog2");

    petNode2:setVisible(false);
    dialogNode1:setVisible(false);
    dialogNode2:setVisible(false);

    -- 左侧飞入
    local petNode1 = findChildByName(self.node, "CT/dialogue/pet1");

    local moveBy = cc.MoveBy:create(0.2, cc.p(-1 * DESIGN_WIDTH / 2, 0));
    local callFunc = cc.CallFunc:create(function()
        -- 隐藏panel
        self.panel:setVisible(false);

        callback();
    end);

    petNode1:runAction(cc.Sequence:create(moveBy, callFunc));
end

-- 右侧飞出
function UIDialogue:rightFlyOut(callback)
    -- 显示panel
    self.panel:setVisible(true);

    local petNode1 = findChildByName(self.node, "CT/dialogue/pet1");
    local dialogNode1 = findChildByName(self.node, "CT/dialogue/dialog1");
    local dialogNode2 = findChildByName(self.node, "CT/dialogue/dialog2");

    petNode1:setVisible(false);
    dialogNode1:setVisible(false);
    dialogNode2:setVisible(false);

    -- 右侧飞出
    local petNode2 = findChildByName(self.node, "CT/dialogue/pet2");

    local moveBy = cc.MoveBy:create(0.2, cc.p(DESIGN_WIDTH / 2, 0));
    local callFunc = cc.CallFunc:create(function()
        -- 隐藏panel
        self.panel:setVisible(false);

        callback();
    end);
    petNode2:runAction(cc.Sequence:create(moveBy, callFunc));
end